// Copyright 2018 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.

package xform

// Factory constructs a normalized expression tree within the memo. As each
// kind of expression is constructed by the factory, it transitively runs
// normalization transformations defined for that expression type. This may
// result in the construction of a different type of expression than what was
// requested. If, after normalization, the expression is already part of the
// memo, then construction is a no-op. Otherwise, a new memo group is created,
// with the normalized expression as its first and only expression.
//
// Factory is largely auto-generated by optgen. The generated code can be found
// in factory.og.go. The factory.go file contains helper functions that are
// invoked by normalization patterns. While most patterns are specified in the
// optgen DSL, the factory always calls the `onConstruct` method as its last
// step, in order to allow any custom manual code to execute.
type Factory struct {
	mem *memo

	// maxSteps sets the maximum number of normalization patterns that the
	// factory will apply. Once this maximum is reached, the factory will
	// construct the requested operator without applying any rewrites to it.
	// This method is useful for debugging, in order to see intermediate
	// optimization steps.
	maxSteps int
}

func newFactory(mem *memo, maxSteps int) *Factory {
	f := &Factory{mem: mem, maxSteps: maxSteps}
	return f
}

// Metadata returns the query-specific metadata, which includes information
// about the columns and tables used in this particular query.
func (f *Factory) Metadata() *Metadata {
	return f.mem.metadata
}

// StoreList allocates storage for a list of group IDs in the memo and returns
// an ID that can be used for later lookup.
func (f *Factory) StoreList(items []GroupID) ListID {
	return f.mem.storeList(items)
}

// InternPrivate adds the given private value to the memo and returns an ID
// that can be used for later lookup. If the same value was added previously,
// this method is a no-op and returns the ID of the previous value.
func (f *Factory) InternPrivate(private interface{}) PrivateID {
	return f.mem.internPrivate(private)
}

// onConstruct is called as a final step by each factory construction method,
// so that any custom manual pattern matching/replacement code can be run.
func (f *Factory) onConstruct(group GroupID) GroupID {
	if f.maxSteps <= 0 {
		return group
	}

	return group
}
